﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nolineq.Iterators
{
    /// <summary>
    /// Iterator Newtonovy metody.
    /// </summary>
    public class NewtonsIterator : Iterator
    {
        public NewtonsIterator(EquationSolver solver)
            :base(solver)
        { }

        /// <summary>
        /// Provede iteraci Newtonovy metody.
        /// </summary>
        /// <returns>Vrati TRUE, pokud se iterace provedla. Jinak vrati FALSE.</returns>
        public override bool Iterate()
        {
            double left = Solver.Start;
            double right = Solver.End;

            // predchozi aproximace
            double oldResult = (left + right) / 2;

            // zkontrolujeme, jesti mame koren
            if (Math.Abs(Solver.Function.Evaluate(oldResult)) < Solver.Precision)
            {
                Solver.Approximation = oldResult;
                return false;
            }

            // zkontrolujeme nenulovou prvni derivaci
            if (Solver.Function.Evaluate1stDerivation(oldResult) == 0)
                return false;

            // vypocet nove aproximace
            double newResult = (-Solver.Function.Evaluate(oldResult) + (Solver.Function.Evaluate1stDerivation(oldResult) * oldResult)) / 
                Solver.Function.Evaluate1stDerivation(oldResult);

            // pokud nam splnuje nova aproximace podminku monitoringu
            if (FunctionMonitorInterval(newResult, oldResult))
            {
                // upravime interval
                if (Math.Abs(newResult - left) < Math.Abs(newResult - right))
                {
                    Solver.End = newResult + Math.Abs(newResult - left);
                }
                else
                {
                    Solver.Start = newResult - Math.Abs(newResult - right);
                }

                // upravime aproximaci
                Solver.Approximation = newResult;
            }
            // jinak podminku monitoringu nesplnuje
            else
            {
                // neprovedeme iteraci
                return false;
            }

            return true;
        }

        /// <summary>
        /// Monitorovani funkce. Sleduje, jestli se nam vylepsuje aproximace korene (v ramci zadaneho intervalu).
        /// </summary>
        /// <param name="newResult">Funkcni hodnota nove aproximace.</param>
        /// <param name="oldResult">Funkcni hodnota stare aproximace.</param>
        /// <returns>Vrati TRUE, pokud je nova aproximace lepsi nez stara. Jinak vrati FALSE.</returns>
        private bool FunctionMonitorInterval(double newResult, double oldResult)
        {
            // pokud novy koren spada do intervalu
            if (newResult >= Solver.Start && newResult <= Solver.End)
            {
                // a pokud splnuje monitoring
                if (Math.Abs(Solver.Function.Evaluate(newResult)) / Math.Abs(Solver.Function.Evaluate(oldResult)) < 1)
                    return true;
                else
                    return false;
            }
            // jinak novy bod tecny nespada do intervalu
            else
            {
                return false;
            }
        }
    }
}
